package com.withoutwords.engine.comminication;

import java.net.URLEncoder;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xml.sax.helpers.ParserFactory;

import com.withoutwords.R;
import com.withoutwords.engine.Base64;
import com.withoutwords.engine.Common;
import com.withoutwords.engine.Game;
import com.withoutwords.engine.Round;
import com.withoutwords.engine.social.SocialUser;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;

public class CommunicationManager {

	public static final String ServerURL = "http://www.faye2.com/bdonkalam/";// "www.bedonkalam.com";

	// Singleton implementation
	private static CommunicationManager commMgrInstance = new CommunicationManager();

	public static CommunicationManager getCommnuicationManager() {
		return commMgrInstance;
	}

	private CommunicationManager() {

	}

	private void runAsyncHttpRequest(final String url,
			final Bundle paramsBundle, final boolean bGetMethod,
			final IHttpResponseHandler handler) {
		Thread th = new Thread(new Runnable() {

			@Override
			public void run() {
				String strURL = url;

				String strParams = "";
				if (paramsBundle != null)
					// adding parameters to URL
					strParams = formatURLParamterstoString(paramsBundle,
							bGetMethod);
				String method = HttpUtility.POST_METHOD;
				if (bGetMethod) {
					method = HttpUtility.GET_METHOD;
					strURL += strParams;
					strParams = "";
				}

				String res = HttpUtility.sendHttpRequest(strURL, strParams,
						method);

				handler.handleHttpResponse(res);

			}
		});

		th.start();
	}

	private void createGame(String strURL, Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, false, new IHttpResponseHandler() {
			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						Bundle bundle = new Bundle();

						int index = response.indexOf(Common.gameIdCode);
						if (index != -1) {
							bundle.putString(
									Common.gameIdCode,
									response.substring(index
											+ Common.gameIdCode.length() + 1));
							listener.onRequestSucceed(
									ServerRequests.createGame, bundle);
							return;
						}

					}
					listener.onRequestFailed(ServerRequests.createGame,  "error");

				}

			}
		});
	}

	private ArrayList<Game> parseGames(String gamesJson)
	{
		ArrayList<Game> games = new ArrayList<Game>();
		try {
			JSONObject jObj = new JSONObject(
					gamesJson);
			JSONArray arr = jObj
					.getJSONArray(Common.gamesCode);
			
			for (int i = 0; i < arr.length(); i++) {
				Game game = new Game();
				game.fromJSON(arr
						.getJSONObject(i));
				games.add(game);
			}
		}
			 catch (Exception e) {
					// TODO: handle exception
				}
		return games;
	}
	private void getUserGames(String strURL, Bundle data,
			final IServerResultListener listener) {

		// HttpUtility util = new HttpUtility(new IHttpResponseHandler() {
		runAsyncHttpRequest(strURL, data, false, new IHttpResponseHandler() {
			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);

						if (index != -1) {
							{
								int codeIndex = index
										+ Common.responseCode.length() + 1;
								String resCode = response.substring(codeIndex,
										codeIndex + 1);

								if (resCode.equalsIgnoreCase("0")) {

									index = response.indexOf(Common.gamesCode);
									if (index != -1) {
										String gamesJson = response
												.substring(index
														+ Common.gamesCode
																.length() + 1);

										ArrayList<Game> games = parseGames(gamesJson);
											Bundle responseBundle = new Bundle();

											responseBundle.putSerializable(
													Common.gamesCode, games);
											listener.onRequestSucceed(
													ServerRequests.getUserGames,
													responseBundle);
											return;
										

									}
								}

								listener.onRequestFailed(
										ServerRequests.getUserGames, "Error");
							}
						}
					}

				}
			}
		});

	}

	private void getOpenedGames(String strURL, Bundle data,
			final IServerResultListener listener) {

		// HttpUtility util = new HttpUtility(new IHttpResponseHandler() {
		runAsyncHttpRequest(strURL, data, false, new IHttpResponseHandler() {
			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);

						if (index != -1) {
							{
								int codeIndex = index
										+ Common.responseCode.length() + 1;
								String resCode = response.substring(codeIndex,
										codeIndex + 1);

								if (resCode.equalsIgnoreCase("0")) {

									index = response.indexOf(Common.gamesCode);
									if (index != -1) {
										String gamesJson = response
												.substring(index
														+ Common.gamesCode
																.length() + 1);

										ArrayList<Game> games = parseGames(gamesJson);
											Bundle responseBundle = new Bundle();

											responseBundle.putSerializable(
													Common.gamesCode, games);
											listener.onRequestSucceed(
													ServerRequests.getOpenedGames,
													responseBundle);
											return;
										

									}
								}

								listener.onRequestFailed(
										ServerRequests.getOpenedGames, "Error");
							}
						}
					}

				}
			}
		});

	}

	private void getRegisteredUser(String strURL, Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, false, new IHttpResponseHandler() {
			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);
						if (index != -1) {
							int codeIndex = index
									+ Common.responseCode.length() + 1;
							String resCode = response.substring(codeIndex,
									codeIndex + 1);

							if (resCode.equalsIgnoreCase("0")) {

								index = response.indexOf(Common.usersCode);
								if (index != -1) {
									String users = response.substring(index
											+ Common.usersCode.length() + 1);
									try {
										JSONObject jsonObj = new JSONObject(
												users);
										JSONArray jsonArr = jsonObj
												.getJSONArray(Common.usersCode);
										ArrayList<SocialUser> registeredUsers = new ArrayList<SocialUser>();
										for (int i = 0; i < jsonArr.length(); i++) {
											SocialUser friend = new SocialUser();
											friend.fromJSON(jsonArr
													.getJSONObject(i));
											registeredUsers.add(friend);

										}
										Bundle bundle = new Bundle();
										bundle.putSerializable(
												Common.usersCode,
												registeredUsers);

										listener.onRequestSucceed(
												ServerRequests.getRegisteredUser,
												bundle);
										return;
									} catch (JSONException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
								}

							}
						}
						{
							listener.onRequestFailed(
									ServerRequests.getRegisteredUser, "");
						}
					}

				}
			}
		});

	}

	private void registerUser(String strURL, Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);
						if (index != -1) {
							int codeIndex = index
									+ Common.responseCode.length() + 1;
							String resCode = response.substring(codeIndex,
									codeIndex + 1);

							if (resCode.equalsIgnoreCase("0")) {

								index = response.indexOf(Common.userIdCode);
								if (index != -1) {
									String userId = response.substring(index
											+ Common.userIdCode.length() + 1);
									Bundle bundle = new Bundle();
									bundle.putString(Common.userIdCode, userId);
									listener.onRequestSucceed(
											ServerRequests.registerUser, bundle);
									return;
								}
							}
						}

						listener.onRequestFailed(ServerRequests.registerUser,
								"Error Registering user");
					}
				}
			}
		});
	}
	private void parseResponseWithNoParameters(String gameId,String response,ServerRequests request,String errorString,IServerResultListener listener)
	{
		if (listener != null) {
			if (response != null) {
				int index = response.indexOf(Common.responseCode);
				if (index != -1) {
					int codeIndex = index
							+ Common.responseCode.length() + 1;
					String resCode = response.substring(codeIndex,
							codeIndex + 1);

					if (resCode.equalsIgnoreCase("0")) {
						Bundle bundle  = new Bundle();
						bundle.putString(
								Common.gameIdCode,
								gameId);
						listener.onRequestSucceed(
								request, bundle);
						return;
					}
				}
				listener.onRequestFailed(request,
						errorString);

			}
		}
	}

	private void joinGame(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.joinGame,"", listener);

				}
			
		});
	}

	private void approveGuess(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.aproveGuess, "", listener);
			}
		});
	}

	private void createRound(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.createRound, "", listener);

			}
		});
	}

	private void submitGuess(String strURL, final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.SubmitGuess, "", listener);

			}
		});
	}

	private void leaveGame(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.leaveGame, " Error Leaving game", listener);

			}
		});
	}

	private void requestCalrification(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.requestClarification, "", listener);
			}
		});
	}
	
	private void sendGuessDirection(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.sendGuessDirection, "", listener);
			}
		});
	}

	
	private void getGamePlayersNames(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);
						if (index != -1) {
							int codeIndex = index
									+ Common.responseCode.length() + 1;
							String resCode = response.substring(codeIndex,
									codeIndex + 1);
							codeIndex += 3 + Common.players.length();

							if (resCode.equalsIgnoreCase("0")) {
								String players = response.substring(codeIndex);
								SocialUser user = null;
								ArrayList<SocialUser> currentPlayers = new ArrayList<SocialUser>();
								try {
									JSONArray JSONResponse = new JSONArray(
											players);
									for (int i = 0; i < JSONResponse.length(); i++) {
										user = new SocialUser();
										user.fromJSON(JSONResponse
												.getJSONObject(i));
										currentPlayers.add(user);
									}
									Bundle bundle = new Bundle();
									bundle.putSerializable(Common.players,
											currentPlayers);
									bundle.putString(
											Common.gameIdCode,
											data.getString(Common.gameIdCode));
									listener.onRequestSucceed(
											ServerRequests.getGamePlayersNames,
											bundle);
									return;
								} catch (JSONException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
						}

					}
					listener.onRequestFailed(
							ServerRequests.getGamePlayersNames, "Error");
				}

			}
		});
	}

	private void getCurrentRoundInfo(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);
						if (index != -1) {
							int codeIndex = index
									+ Common.responseCode.length() + 1;
							String resCode = response.substring(codeIndex,
									codeIndex + 1);

							if (resCode.equalsIgnoreCase("0")) {

								index = response.indexOf(Common.roundCode);
								if (index != -1) {
									String roundJson = response.substring(index
											+ Common.roundCode.length() + 1);
									Bundle bundle = new Bundle();
									Round r = new Round();
									try {
										JSONObject jObj = new JSONObject(
												roundJson);

										r.fromJSON(jObj);

										byte[] drawingBytes = Base64.decode(
												(String) jObj
														.get(Common.LastActingData),
												Base64.NO_WRAP
														| Base64.URL_SAFE);
										bundle.putByteArray(
												Common.drawingBytes,
												drawingBytes);

									} catch (JSONException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}

									bundle.putSerializable(Common.roundCode, r);
									bundle.putString(
											Common.gameIdCode,
											data.getString(Common.gameIdCode));
									listener.onRequestSucceed(
											ServerRequests.getCurrentRoundInfo,
											bundle);
									return;
								}
							}
						}

						
					}
					listener.onRequestFailed(
							ServerRequests.getCurrentRoundInfo, "Error");
				}
			}
		});
	}

	private void sendActingData(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, false, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				parseResponseWithNoParameters(data.getString(Common.gameIdCode),response, ServerRequests.sendActingData, "", listener);
			}
		});
	}

	private void getLastActingData(String strURL,final Bundle data,
			final IServerResultListener listener) {

		runAsyncHttpRequest(strURL, data, true, new IHttpResponseHandler() {

			@Override
			public void handleHttpResponse(String response) {
				if (listener != null) {
					if (response != null) {
						int index = response.indexOf(Common.responseCode);
						if (index != -1) {
							int codeIndex = index
									+ Common.responseCode.length() + 1;
							String resCode = response.substring(codeIndex,
									codeIndex + 1);

							if (resCode.equalsIgnoreCase("0")) {
								/*
								 * { wordIndex:3, gameId:XXXX,
								 * Drawing:base64_bytes
								 * 
								 * }
								 */
								Bundle bundle = new Bundle();
								index = response.indexOf(Common.LastActingData);
								if (index != -1) {

									String JsonString = response
											.substring(index
													+ Common.LastActingData
															.length() + 1);
									try {
										JSONObject jObj = new JSONObject(
												JsonString);
										/*
										 * byte[] drawingBytes = Base64.decode(
										 * (String) jObj
										 * .get(Common.drawingBytes
										 * ),//Base64.DEFAULT); Base64.NO_WRAP|
										 * Base64.URL_SAFE );
										 */
										String drawingStr = jObj
												.getString(Common.drawingBytes);

										/*
										 * bundle.putByteArray(
										 * Common.drawingBytes, drawingBytes);
										 */
										bundle.putString(Common.drawingBytes,
												drawingStr);
										int wordIndex = jObj
												.getInt(Common.wordIndex);
										bundle.putInt(Common.wordIndex,
												wordIndex);
										bundle.putString(
												Common.gameIdCode,
												data.getString(Common.gameIdCode));

									} catch (JSONException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
									listener.onRequestSucceed(
											ServerRequests.getLastActingData,
											bundle);
									return;
								}
							}
						}
					}
					listener.onRequestFailed(ServerRequests.getLastActingData,
							"");
				}

			}

		});
	}

	public boolean isConnectionAvailable(Context context) {
		ConnectivityManager mgr = ((ConnectivityManager) context
				.getSystemService(Context.CONNECTIVITY_SERVICE));
		NetworkInfo inf = mgr.getActiveNetworkInfo();
		boolean infAvailable = (inf != null);
		return infAvailable;
	}

	public boolean request(Context context, final ServerRequests request,
			Bundle data, final IServerResultListener listener) {

		if (context != null && isConnectionAvailable(context)) {

			String strURL = ServerURL + request + ".php";

			switch (request) {
			case getUserGames: {
				getUserGames(strURL, data, listener);
				break;
			}
			case getOpenedGames: {
				getOpenedGames(strURL, data, listener);
				break;
			}
			case getRegisteredUser: {
				getRegisteredUser(strURL, data, listener);
				break;
			}
			case createGame: {

				createGame(strURL, data, listener);
				break;
			}
			case registerUser: {

				registerUser(strURL, data, listener);
				break;

			}
			case joinGame: {
				joinGame(strURL, data, listener);
				break;
			}
			case createRound: {
				createRound(strURL, data, listener);
				break;
			}
			case sendActingData: {
				sendActingData(strURL, data, listener);
				break;
			}
			case getCurrentRoundInfo: {
				getCurrentRoundInfo(strURL, data, listener);
				break;
			}
			case aproveGuess: {
				approveGuess(strURL, data, listener);
				break;
			}
			case SubmitGuess: {
				submitGuess(strURL, data, listener);
				break;
			}
			case getLastActingData: {

				getLastActingData(strURL, data, listener);
				break;
			}

			case leaveGame: {
				leaveGame(strURL, data, listener);
				break;
			}
			case getGamePlayersNames: {
				getGamePlayersNames(strURL, data, listener);
				break;
			}
			case requestClarification:
			{
				requestCalrification(strURL, data, listener);
				break;
			}
			case sendGuessDirection:
			{
				sendGuessDirection(strURL, data, listener);
				break;
			}
			}
			return true;
		} else {
			if (listener != null && context != null)
				listener.onRequestFailed(request,
						context.getString(R.string.error_conn));
			return false;
		}

	}

	String formatURLParamterstoString(Bundle params, boolean bgetrequest) {
		if (!params.isEmpty()) {
			String strParams = "";
			if (bgetrequest)
				strParams = "?";

			for (String key : params.keySet()) {
				try {
					String value = params.get(key).toString();
					if (value != null) {
						if (bgetrequest)
							value = URLEncoder.encode(value, "utf-8");
						strParams += key + "=" + value;
						strParams += "&";
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			// to remove the last &
			strParams = strParams.substring(0, strParams.length() - 1);

			return strParams;
		} else
			return "";
	}

}
